package com.mapforge.gui.map;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Iterator;

import javax.swing.event.UndoableEditEvent;

import com.mapforge.gui.interfaces.MapToolGUIInterface;
import com.mapforge.gui.interfaces.TileMapEditorGUIInterface;
import com.mapforge.gui.tools.SelectedTiles;
import com.mapforge.listener.MapToolChangeEvent;
import com.mapforge.listener.RenderEvent;
import com.mapforge.listener.TileLayerCreationEvent;
import com.mapforge.listener.TileLayerDeletionEvent;
import com.mapforge.listener.TileLayerEditEvent;
import com.mapforge.listener.TileMapCreationEvent;
import com.mapforge.listener.TileSelectionEvent;
import com.mapforge.map.TileLayer;
import com.mapforge.map.TileMap;
import com.mapforge.map.tools.MapToolInterface;
import com.mapforge.map.tools.tilewriting.AbstractTileWriterTool;
import com.mapforge.selection.ComparablePoint;
import com.mapforge.selection.Selection;

/**
 * 
 * @author Will Morrison
 */
public class TileMapEditorPanel extends TileMapPanel implements
		TileMapEditorGUIInterface {
	/**
	 * Default serial version ID, here to remove warnings.
	 */
	private static final long serialVersionUID = 1L;

	private Selection tileSelection;

	private Point mousePoint;
	private MapToolInterface mapTool;

	public TileMapEditorPanel() {
		this(null);
	}

	public TileMapEditorPanel(TileMap tilemap) {
		super(tilemap);
		this.setBackground(Color.BLACK);
		mousePoint = new Point(-1, -1);
	}

	/*
	 * protected void drawTileRect(Graphics2D g) { if (tileSelection != null) {
	 * Rectangle r = tileSelection.getBoundingRectangle(); Iterator<? extends
	 * Point> it = tileSelection.iterator(); Point p; while (it.hasNext()) { p =
	 * it.next(); drawTileRect(mousePoint.x + p.x - r.x, mousePoint.y + p.y -
	 * r.y, g); } } }
	 */

	/**
	 * Draws a simple rectangle with dimension equal to the current tile width
	 * and height. This outlines the current tile.
	 * 
	 * @param x
	 *            upper left location of rectangle
	 * @param y
	 *            upper left y location of rectangle
	 * @param g
	 *            graphics object rendering rectangle
	 */
	protected void drawTileRect(int x, int y, Graphics g) {
		TileMap tilemap = getTiledMap();
		if (tilemap != null) {
			int tW = (int) (tilemap.getTileWidth() / tilemap.getScale());
			int tH = (int) (tilemap.getTileHeight() / tilemap.getScale());
			x = (int) (x * tW);
			y = (int) (y * tH);
			g.setColor(Color.BLACK);
			g.drawRect(x, y, tW, tH);
			g.setColor(Color.WHITE);
			g.drawRect(x + 1, y + 1, tW - 2, tH - 2);
			g.drawRect(x - 1, y - 1, tW + 2, tH + 2);
		}
	}

	protected void drawTileRect(Selection selection, Graphics g) {
		TileMap tilemap = getTiledMap();
		if (tilemap != null) {
			int tW = (int) (tilemap.getTileWidth() / tilemap.getScale());
			int tH = (int) (tilemap.getTileHeight() / tilemap.getScale());
			int x;
			int y;
			ComparablePoint p;
			Iterator<ComparablePoint> it = selection.iterator();
			while (it.hasNext()) {
				p = it.next();
				x = p.x * tW;
				y = p.y * tH;
				g.setColor(Color.BLACK);
				g.drawRect(x, y, tW, tH);
				g.setColor(Color.WHITE);
				g.drawRect(x + 1, y + 1, tW - 2, tH - 2);
				g.drawRect(x - 1, y - 1, tW + 2, tH + 2);
			}
		}
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		if (mapTool != null && mapTool.isEditing()
				&& mapTool instanceof AbstractTileWriterTool) {
			AbstractTileWriterTool tool = (AbstractTileWriterTool) mapTool;
			tool.paintTilePreview((Graphics2D) g);
		}
		SelectedTiles tiles = SelectedTiles.getHandle();
		if (!tiles.isEmpty()) {
			drawTileRect(tiles, g);
		}
	}

	@Override
	public void tileMapCreationPerformed(TileMapCreationEvent mapCreationEvent) {
		this.setTiledMap(mapCreationEvent.getTileMapCreated());
	}

	/**
	 * Returns the current tile relative to some point. Equivalent to calling
	 * tileAt(p.x,p.y)
	 * 
	 * @param p
	 *            some point
	 * @return a point location of the current tile
	 */
	public Point tileAt(Point p) {
		return tileAt(p.x, p.y);
	}

	/**
	 * Returns the current tile x and y locations relative to an x and y
	 * location.
	 * 
	 * @param x
	 *            x location
	 * @param y
	 *            y location
	 * @return the x and y location of the current tile
	 */
	public Point tileAt(double x, double y) {
		TileMap tileMap = getTiledMap();
		if (tileMap != null) {
			int w = tileMap.getWidth();
			int h = tileMap.getHeight();
			if (x < tileMap.getX() || x > tileMap.getX() + w
					|| y < tileMap.getY() || y > tileMap.getY() + h) {
				return null;
			}
			Point tileAt = new Point();
			// System.out.println(tileMap.getX() + " " + x + " " + w);
			tileAt.x = (int) ((tileMap.getX() + x) / tileMap.getTileWidth());
			tileAt.y = (int) ((tileMap.getY() + y) / tileMap.getTileHeight());
			// System.out
			// .println("TileMapEditorPanel: tileAt " + tileAt.x + " "
			// + tileAt.y + " " + this.getWidth() + " "
			// + this.getHeight());
			// System.out.println("TileMapEditorPanel tileAt: " + tileAt);
			return tileAt;
		} else {
			return null;
		}
	}

	@Override
	public void tileSelectionPerformed(TileSelectionEvent tileSelectionEvent) {
		tileSelection = tileSelectionEvent.getTileSelection();
	}

	@Override
	public void tileLayerCreationPerformed(TileLayerCreationEvent tileLayerEvent) {
		this.getTiledMap().addTileLayer(tileLayerEvent.getCreatedTileLayer());
	}

	@Override
	public void tileLayerDeletionPerformed(TileLayerDeletionEvent event) {
		Iterator<TileLayer> it = event.getDeletedLayerIterator();
		while (it.hasNext()) {
			this.getTiledMap().removeTileLayer(it.next());
		}
	}

	@Override
	public void tileLayerEditPerformed(TileLayerEditEvent layerChangeEvent) {
		repaint();
	}

	@Override
	public void undoableEditHappened(UndoableEditEvent e) {
		revalidate();
		repaint();
	}

	@Override
	public void mapToolChangePerformed(MapToolChangeEvent e) {
		this.mapTool = e.getMapTool();
		Object obj = e.getSource();
		if (obj instanceof MapToolGUIInterface) {
			MapToolGUIInterface rep = (MapToolGUIInterface) obj;
			this.setCursor(rep.getMapToolCursor());
		}
	}

	@Override
	public void renderEventOccurred(RenderEvent repaintEvent) {
		repaint();
		revalidate();
	}

	@Override
	public Rectangle getVisibleTileRectangle() {
		Rectangle rect = this.getVisibleRect();
		Rectangle rectangle = new Rectangle(rect);
		TileMap tileMap = this.getTiledMap();
		int tileW = tileMap.getTileWidth();
		int tileH = tileMap.getTileHeight();
		rectangle.x /= tileW;
		rectangle.y /= tileH;
		rectangle.width /= tileW;
		rectangle.height /= tileH;
		return rectangle;
	}

	@Override
	public Point getMouseTileAtPosition() {
		Point p = this.getMousePosition();
		TileMap tileMap = this.getTiledMap();
		p.x /= tileMap.getTileWidth();
		p.y /= tileMap.getTileHeight();
		return p;
	}

	@Override
	public Point getLastMouseClickTileAtPosition() {
		return this.mousePoint;
	}

	@Override
	public Point convertPointToTileCoordinates(int x, int y) {
		TileMap tileMap = this.getTiledMap();
		return new Point(x / tileMap.getTileWidth(), y
				/ tileMap.getTileHeight());
	}
}